/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import request from '@ohos.request';
import image from '@ohos.multimedia.image';
import fileio from '@ohos.file.fs';
import { BitmapDisplayConfig } from '../BitmapDisplayConfig';
import { BitmapGlobalConfig } from '../BitmapGlobalConfig'
import { BitmapLoadTask } from '../../task/BitmapLoadTask'
import { BitmapLoadCallBack } from '../callback/BitmapLoadCallBack'
import { MD5 } from '../../util/md5'
import { LruDiskCache } from '../../cache/LruDiskCache'
import { LruMemoryCache } from '../../cache/LruMemoryCache'

export class BitmapCache {
    private DISK_CACHE_INDEX: number = 0;
    private mDiskCacheLock: object = null
    private globalConfig: BitmapGlobalConfig;
    private mContext: any;
    private TAG = "BitmapCache"
    private savePath: string = null
    private mLruDiskCache: LruDiskCache
    private mLruCache: LruMemoryCache<string, any>
    private mImageSuffix = ""
    private PROGRESS_LOAD_STARTED = 0;

    constructor(globalConfig: BitmapGlobalConfig) {
        if (globalConfig == null) throw new Error("globalConfig may not be null");
        this.globalConfig = globalConfig;
    }

    public initMemoryCache(): void {
        if (!this.globalConfig.isMemoryCacheEnabled()) return;
        this.mLruCache = new LruMemoryCache(this.globalConfig.memoryCacheSize)
    }

    public initDiskCache(): void {
        if (!this.globalConfig.isDiskCacheEnabled()) return;
        try {
          let exit =  fileio.accessSync(globalThis.filesDir + "/" + "xBitmapCache" + "/journal.txt")
            if(!exit){
                fileio.mkdir(globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath(), (err) => {
                    if (err) {
                        console.info("BitmapCache mkdir failed with error message: " + err.message + ", error code: " + err.code);
                    } else {
                        this.mLruDiskCache = new LruDiskCache(this.globalConfig.getDiskCachePath(), this.globalConfig.getAppVersion(), this.globalConfig.getDiskCacheSize())
                        console.info("BitmapCache mkdir sucess")
                    }

                })
            }
        } catch (e) {
            fileio.mkdir(globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath(), (err) => {
                if (err) {
                    console.info("BitmapCache mkdir failed with error message: " + err.message + ", error code: " + err.code);
                } else {
                    this.mLruDiskCache = new LruDiskCache(this.globalConfig.getDiskCachePath(), this.globalConfig.getAppVersion(), this.globalConfig.getDiskCacheSize())
                    console.info("BitmapCache mkdir sucess")
                }

            })
        }
    }

    public downloadBitmap(path: string, config: BitmapDisplayConfig, task: BitmapLoadTask, callBack: BitmapLoadCallBack) {
        if (this.mLruDiskCache == null) {
            this.mLruDiskCache = new LruDiskCache()
        }
        task.onProgressUpdate([this.PROGRESS_LOAD_STARTED])
        let filesize = -1;
        let filepath = globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath() + "/" + this.getMD5Value(path) + this.getSuffix(path)
        let exits = fileio.accessSync(filepath);
        if (exits){
            fileio.unlinkSync(filepath)
        }
        request.downloadFile(globalThis.context, {
            url: path,
            filePath: filepath
        }).then((loadTask) => {
            loadTask.on("progress", function (receivedSize, totalSize) {
                filesize = totalSize;
                if (config.getLoadingPixMapisNull()) {
                    callBack.onLoadingResource(config.getLoadingDrawable());
                } else {
                    callBack.onLoadingPixelMap(config.getLoadingPixMap())
                }
                callBack.onProgressUpdate(receivedSize, totalSize)
            })

            loadTask.on("complete", () => {
                this.mLruDiskCache.addImageItem(this.getMD5Value(path), filesize)
                var fd = fileio.openSync(globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath() + "/" + this.getMD5Value(path) + this.getSuffix(path));
                var imageSource = image.createImageSource(fd.fd);
                imageSource.createPixelMap((err, pixelMap) => {
                    if (!err) {
                        //添加至数组中 md5的图片名称
                        this.mLruCache.put(this.getMD5Value(path), pixelMap)
                        callBack.onLoadCompleted("", pixelMap)
                    } else {
                        console.error("Failed to create a PixelMap." + err);
                    }
                })

            })
        }).catch((e) => {
            if (config.getLoadFailedPixMapisNull()) {
                callBack.onLoadFailedResource(config.getLoadFailedDrawable())
            } else {
                callBack.onLoadFailedPixelMap(config.getLoadFailedPixelMap())
            }
        })
    }

    public getBitmapFromMemCache(uri: string, func: (pixMap: PixelMap) => void) {
        if (this.globalConfig.getDefaultCacheExpiry() != -1) {
            if (this.mLruDiskCache == null) {
                this.mLruDiskCache = new LruDiskCache()
            }
            let imagepath = globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath() + "/" + this.getMD5Value(uri) + this.getSuffix(uri)
            let access = this.mLruDiskCache.getFileAccess(imagepath) //文件是否存在
            let imageName = this.getMD5Value(uri)
            if (access) {
                let value = this.mLruDiskCache.getFileMessage(globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath() + "/" + this.getMD5Value(uri) + this.getSuffix(uri))
                let time = new Date().getTime(); //毫秒数
                let status = ((time / 1000) - value.ctime) >= this.globalConfig.getDefaultCacheExpiry() ? true : false;
                if (status) {
                    if (this.mLruCache != null || this.mLruCache != undefined || this.mLruCache.get(this.getMD5Value(uri)) != null) {
                        this.mLruCache.remove(this.getMD5Value(uri)) // 删缓存
                    }
                    let getImageSize = this.getImageSize(this.getMD5Value(uri))
                    this.mLruDiskCache.DeleteItemImage(imageName, getImageSize) //删磁盘缓存
                    return func(null)
                } else {
                    let My_image = this.mLruCache.get(this.getMD5Value(uri))
                    if (My_image == null) {
                        if (uri == null || !this.globalConfig.isDiskCacheEnabled()) return func(null);
                        let path = globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath() + "/" + this.getMD5Value(uri) + this.getSuffix(uri);
                        let file = fileio.openSync(path);
                        let imageSource = image.createImageSource(file.fd);
                        imageSource.createPixelMap((err, pixelMap: image.PixelMap) => {
                            return func(pixelMap)
                        })
                    }
                    else {
                        return func(My_image)
                    }
                }
            } else {
                return func(null)
            }

        } else {
            if (uri != null || uri != undefined) {
                try {
                    let image = this.mLruCache.get(this.getMD5Value(uri))
                    if (image == null) {
                        image = this.getBitmapFromDiskCache(uri,image)
                    }
                    return func(image)

                } catch (e) {
                    return func(null)
                }
            } else {
                return func(null)
            }
        }
    }

    public getBitmapFromDiskCache(uri: string, image) {
        if (uri == null || !this.globalConfig.isDiskCacheEnabled()) return null;
        var fd = fileio.openSync(globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath() + "/" + this.getMD5Value(uri));
        var imageSource = image.createImageSource(fd);
        imageSource.createPixelMap(function (err, pixelMap) {
            if (err) {
                image = null;
            } else {
                image = pixelMap

            }
            return  image;
        })
    }

    public clearCache(url?: string): void {
        this.clearMemoryCache(url);
        this.clearDiskCache(url);
    }
    //清除内存缓存
    public clearMemoryCache(url): void {
        if (url == null || url == undefined) {
            this.mLruCache.evicAll()
        } else {
            this.mLruCache.remove(this.getMD5Value(url))
        }
    }

    public clearDiskCache(imageName?: string): void {
        if (imageName != null) {
            this.mLruDiskCache.DeleteItemImage(imageName, this.getImageSize(imageName))
        } else {
            this.mLruDiskCache.DeleteAllImage(true, globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath() + "/", false)
        }

    }

    public flush(): void {
        this.globalConfig.setMemoryCacheEnabled(false)
    }

    public close(): void {
        this.globalConfig.setMemoryCacheEnabled(false)
    }

    public getBitmapFileFromDiskCache(uri: string): string {
        if (uri == null)return
        return
        globalThis.filesDir + this.globalConfig.getDiskCachePath();
    }

    private getMD5Value(value: string): any {
        let names = value.split("/")
        let name = names[names.length -1].toString()
        return MD5.prototype.hex_md5(name)
    }

    private getSuffix(value: string): any {
        let mArray = value.split("/")
        let v = mArray[mArray.length -1].split(".")
        return "." + v[1]
    }

    private getImageSize(imageName: string): number {
        let size = 0;
        fileio.unlink(globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath() + "/" + imageName)
        fileio.stat(globalThis.filesDir + "/" + this.globalConfig.getDiskCachePath() + "/" + imageName, (err, stat) => {
            size = stat.size;
        })
        return size
    }
}